//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public License
// along with this program.  If not, see http://www.gnu.org/licenses/.
// 

#include "Src.h"

Define_Module(Src);

void Src::initialize()
{
	// TODO - Generated method body
	selfID=par("srcID");
	if(selfID==0)
	{
		initial=new cMessage();
		scheduleAt(0,initial);
	}
	g=gate("out");
	channel = (cDatarateChannel*)g->getTransmissionChannel();
	ID=par("srcX");
	maxRate=par("maxRate");
	rateLimiter.state=0;
	rateLimiter.crate=maxRate;
	rateLimiter.trate=maxRate;
	rateLimiter.si_count=0;
	rateLimiter.timer_scount=0;
	bc_limit=par("BC_limit");
	MIN_DEC_FACTOR=0.5;
	MIN_RATE=10000000;
	TIMER_PERIOD=0.015;
	FAST_RECOVERY_TH=5;
	R_AI=5000000;
	R_HAI=50000000;


}

void Src::handleMessage(cMessage *msg)
{
	// we want to send msg to switch
	if(msg->isSelfMessage())
	{

		if(msg->getKind()==3)  //time expired
		{
			cancelAndDelete(msg);
			timer_expired();

		}else
		{
			if(channel->isBusy()==true)
			{

			//	scheduleAt(channel->getTransmissionFinishTime(),msg); //line is busy->scedule again
				EV<< "The ID:  "<< ID << " ***fucked*** CheckGate:  "<< chk<< " SImetime is:  "<<simTime() << endl;

			}else
			{
				//make new message to send
				Eth_pck *toSend=new Eth_pck();
				toSend->setByteLength(1500);
				toSend->setDstID(par("destX"));
				toSend->setSrcID(ID);
				transFrame(toSend); //bla bla
				channel->setDatarate(this->rateLimiter.crate);
				EV<<"this is the data rate"<<channel->getDatarate()<<endl;
				send(toSend,"out");
				chk=channel->getTransmissionFinishTime() ;
				scheduleAt(chk ,msg);
				EV<< "The ID:  "<<ID <<"  (sent) CheckGate:  "<< chk <<" SImetime is:  "<< simTime()<<endl;
			}
		}

	}else  //we receive msg from switch --> check if its RateLimit or regular
	{

		FBFrame *fbFrame;
		fbFrame=dynamic_cast<FBFrame *>(msg);

		if(fbFrame!=NULL)
		{
			handleFB(fbFrame);
			delete(msg);

		}else
		{
			delete(msg);
		}

	}

}
int Src::MIN(int X,int Y)
{
	return ((X) < (Y) ?  (X) : (Y));
}

void Src::handleFB(FBFrame *fbFrame)
{
	GD=0.5 / fbFrame->getFb();

	if(this->rateLimiter.state==0)
	{
		if(fbFrame->getFb()!=0 && fbFrame->getQoff()<0)
		{
			this->rateLimiter.state=1;
			this->rateLimiter.crate=maxRate;
			this->rateLimiter.trate=maxRate;
			this->rateLimiter.tx_bcount=bc_limit;
			this->rateLimiter.si_count=0;
		}
	}

	if(fbFrame->getFb()!=0)
	{
		if(this->rateLimiter.si_count!=0)
		{
			this->rateLimiter.trate=this->rateLimiter.crate;
			this->rateLimiter.tx_bcount=bc_limit;
		}

		this->rateLimiter.si_count=0;
		this->rateLimiter.timer_scount=0;

		dec_factor=(1-GD*fbFrame->getFb());
		if(dec_factor< MIN_DEC_FACTOR)
		{
			dec_factor=MIN_DEC_FACTOR;
		}
		this->rateLimiter.crate=(this->rateLimiter.crate * dec_factor);
		if(this->rateLimiter.crate< MIN_RATE)
		{
			this->rateLimiter.crate=MIN_RATE;
		}

		cMessage *note= new cMessage();
		note->setKind(3);
		scheduleAt(simTime()+TIMER_PERIOD,note);
	}

}



void Src::timer_expired()
{
	double expired_period;
	double rnd;
	rnd =par("random" );

	if(this->rateLimiter.state==1)
	{
		this->rateLimiter.timer_scount++;
		self_increase();

		//reset the timer , margin of 30% randomness
		if(this->rateLimiter.timer_scount<FAST_RECOVERY_TH)
		{
			expired_period=rnd*TIMER_PERIOD;
		}else
		{
			expired_period=rnd*TIMER_PERIOD/2;
		}

		cMessage *note= new cMessage();
		note->setKind(3);
		scheduleAt(simTime()+expired_period,note);
	}

}

void Src::self_increase()
{
	int to_count;
	double Ri;

	to_count=MIN(this->rateLimiter.si_count,this->rateLimiter.timer_scount);

	if(this->rateLimiter.si_count>FAST_RECOVERY_TH  || this->rateLimiter.timer_scount>FAST_RECOVERY_TH)
	{
		if(this->rateLimiter.si_count>FAST_RECOVERY_TH && this->rateLimiter.timer_scount>FAST_RECOVERY_TH)
		{
			//hyperactive increase
			Ri=R_HAI* (to_count-FAST_RECOVERY_TH);
		}else
		{
			//active increase
			Ri=R_AI;
		}

	}else
	{
		Ri=0;
	}

	if( (this->rateLimiter.si_count==1 || this->rateLimiter.timer_scount==1) && this->rateLimiter.trate>10*this->rateLimiter.crate)
	{
		this->rateLimiter.trate=this->rateLimiter.trate/8;
	}else
	{
		this->rateLimiter.trate=this->rateLimiter.trate+Ri;
	}

	this->rateLimiter.crate=(this->rateLimiter.trate+this->rateLimiter.crate)/2;

	if(this->rateLimiter.crate>maxRate)
	{
		this->rateLimiter.crate=maxRate;
	}

}

void Src::transFrame(Eth_pck *msg)
{
	double expire_thresh;
	double rnd;
	rnd =par("random");

	if(this->rateLimiter.crate==maxRate )
	{
		this->rateLimiter.state=0;
		this->rateLimiter.crate=maxRate;
		this->rateLimiter.trate=maxRate;
		this->rateLimiter.tx_bcount=bc_limit;
		this->rateLimiter.si_count=0;
		//this->rateLimiter.timer=
	}else
	{
		this->rateLimiter.tx_bcount -= msg->getByteLength();


		if(this->rateLimiter.tx_bcount<0)
		{
			this->rateLimiter.si_count++;
			if(this->rateLimiter.si_count<FAST_RECOVERY_TH)
			{
				expire_thresh=rnd*bc_limit;
			}else
			{
				expire_thresh=rnd*bc_limit/2;
			}

			this->rateLimiter.tx_bcount=expire_thresh;
			self_increase();
		}

	}

}
